﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace urBook.Engine
{
    /// <summary>
    /// Static class provides extension methods for Address Book classes.
    /// </summary>
    public static class Utilities
    {
        /// <summary>
        /// Extension method for class AddressBook. 
        /// Formats text in contact cards and groups and remove unnecessary characters in phone numbers.
        /// <param name="a">AddressBook to format</param>
        /// </summary>
        public static void format(this AddressBook a)
        {
            foreach (ContactCard c in a.contacts)
                format(c);
            foreach (Group g in a.groups)
                format(g);
        }
        private static void format(this Group g)
        {
            g.name = capitalize(g.name);
        }
        /// <summary>
        /// Extension method for class AddressBook. 
        /// Formats text in contact cards and groups and remove unnecessary characters in phone numbers.
        /// <param name="a">AddressBook to format</param>
        /// </summary>
        public static void format(this ContactCard c)
        {
            c.name.firstName = capitalize(c.name.firstName);
            c.name.lastName = capitalize(c.name.lastName);
            c.company = capitalize(c.company);
            if (c.name.firstName == "" || c.name.firstName == " ")
            {
                try
                {

                    string[] name = c.name.lastName.Split(new char[] { ' ' }, 2);
                    c.name.firstName = name[0];
                    c.name.lastName = name[1];
                }
                catch (Exception a) { }
            }
            else if (c.name.lastName == "" || c.name.lastName == " ")
            {
                try
                {
                    string[] name = c.name.firstName.Split(new char[] { ' ' }, 2);
                    c.name.firstName = name[0];
                    c.name.lastName = name[1];
                }
                catch (Exception a) { }
            }
            foreach (Telephone t in c.phones)
            {
                t.value = t.value.Replace(" ", "");
                t.value = t.value.Replace("-", "");
                t.value = t.value.Replace("(", "");
                t.value = t.value.Replace(")", "");
                t.value = t.value.Replace(".", "");
                if (t.value.StartsWith("00"))
                    t.value = "+" + t.value.Substring(2);
            }
        }
        private static string capitalize(string s)
        {
            if (s == null)
                return "";
            if (s.Length == 0)
                return s;

            string r = "";
            string[] splits = s.Trim().Split(' ');
            foreach (string sp in splits)
                if(sp.Length > 1)
                    r += char.ToUpper(sp[0]) + sp.Substring(1).ToLower() + " ";

            return r.Trim();
        }

        public static void appendCountryCode(this AddressBook a, string countryCode)
        {
            foreach (ContactCard c in a.contacts)
                appendCountryCode(c, countryCode);
        }
        public static void appendCountryCode(this ContactCard c, string countryCode)
        {
            if (countryCode.StartsWith("+")) countryCode = countryCode.Substring(1);
            if (countryCode.StartsWith("00")) countryCode = countryCode.Substring(2);

            foreach (Telephone p in c.phones)
                if (!p.value.StartsWith("+"))
                    if (p.value.StartsWith("00"))
                        p.value = "+" + p.value.Substring(2);
                    else if (p.value.StartsWith("0"))
                        p.value = "+" + countryCode + p.value.Substring(1);
                    else
                        p.value = "+" + countryCode + p.value;
        }

        public static List<ContactCard> findDuplicates(this AddressBook addressBook)
        {
            List<ContactCard> dups = new List<ContactCard>();

            for (int i = 0; i < addressBook.contacts.Count; i++)
            {
                ContactCard c1 = addressBook.contacts[i];
                bool duplicateC1 = false;
                
                for (int j = i + 1; j < addressBook.contacts.Count; j++)
                {
                    ContactCard c2 = addressBook.contacts[j];
                    bool duplicateC2 = false;
                    
                    #region Name
                    if (c1.name.formattedName.ToLower().Trim().Equals(c2.name.formattedName.ToLower().Trim()))
                        duplicateC2 = true;
                    #endregion
                    #region Telephone
                    foreach (Telephone t1 in c1.phones)
                        foreach (Telephone t2 in c2.phones)
                            if (t1.value.Equals(t2.value))
                                duplicateC2 = true;
                    #endregion
                    #region Email
                    foreach (Email email1 in c1.emails)
                        foreach (Email email2 in c2.emails)
                            if (email1.value.Equals(email2.value))
                                duplicateC2 = true;
                    #endregion
                    
                    if (duplicateC2)
                    {
                        if (!dups.Contains(c2))
                            dups.Add(c2);
                        duplicateC1 = true;
                    }
                }

                if (duplicateC1)
                    if(!dups.Contains(c1))
                        dups.Add(c1);
            }
            return dups;
        }

        public static ContactCard merge(this AddressBook addressBook, List<ContactCard> duplicates)
        {
            ContactCard result = new ContactCard();

            foreach (ContactCard c in duplicates)
            {
                result.name = new Name();
                if (c.name.lastName != null && c.name.lastName != "")
                    result.name.lastName = c.name.lastName;
                if (c.name.firstName != null && c.name.firstName != "")
                    result.name.firstName = c.name.firstName;
                if (c.company != null && c.company != "")
                    result.company = c.company;
                if (c.title != null && c.title != "")
                    result.title = c.title;
                if (c.homeAddress != null)
                    result.homeAddress = c.homeAddress;
                if (c.workAddress != null)
                    result.workAddress = c.workAddress;
                if (c.photo != null)
                    result.photo = c.photo;
                foreach (Telephone t in c.phones)
                    if (!result.phones.Any(t1 => t1.value.Equals(t.value)))
                        result.phones.Add(t);
                foreach (Email e in c.emails)
                    if (!result.emails.Any(e1 => e1.value.Equals(e.value)))
                        result.emails.Add(e);
                foreach (string gname in c.groups)
                {
                    getGroupByName(addressBook, gname).contacts.Remove(c);
                    getGroupByName(addressBook, gname).addToGroup(result);
                }
                if (c.url != null && c.url != "")
                    result.url = c.url;
                if (c.notes != null && c.notes != "")
                    result.notes += c.notes;
            }

            return result;
        }

        public static AddressBook load(string path)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(AddressBook));
            FileStream fs = new FileStream(path, FileMode.Open);
            AddressBook addressBook = (AddressBook)serializer.Deserialize(fs);
            fs.Close();
            return addressBook;
        }

        public static ContactCard loadContact(string path)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(ContactCard));
            FileStream fs = new FileStream(path, FileMode.Open);
            ContactCard contact = (ContactCard)serializer.Deserialize(fs);
            fs.Close();
            return contact;
        }

        public static Hashtable loadCountries(string path)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(path);
            XmlNodeList countriesList = doc.SelectNodes("Countries/Country");
            Hashtable countries = new Hashtable();
            foreach (XmlNode country in countriesList)
            {
                string countryName = country.ChildNodes[0].InnerText;
                string countryCode = country.ChildNodes[1].InnerText;
                countries[countryName] = countryCode;
            }
            return countries;
        }

        public static void save(this AddressBook addressbook, string path)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(AddressBook));
            FileStream fs = new FileStream(path, FileMode.Create);
            serializer.Serialize(fs, addressbook);
            fs.Close();
        }

        public static void save(this ContactCard contactCard, string path)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(ContactCard));
            FileStream fs = new FileStream(path, FileMode.Create);
            serializer.Serialize(fs, contactCard);
            fs.Close();
        }

        public static Group getGroupByName(this AddressBook addressBook, string name)
        {
            foreach (Group g in addressBook.groups)
                if (g.name == name)
                    return g;

            Group group = new Group();
            addressBook.groups.Add(group);
            group.name = name;
            return group;
        }

        public static void add(this ContactCard c, Telephone t)
        {
            c.phones.Add(t);
        }
        public static void add(this ContactCard c, Email e)
        {
            c.emails.Add(e);
        }
        public static void add(this Group g, ContactCard c)
        {
            g.addToGroup(c);
        }
        public static void add(this AddressBook a, ContactCard c)
        {
            a.contacts.Add(c);
        }

        public static void remove(this ContactCard c, Telephone t)
        {
            c.phones.Remove(t);
        }
        public static void remove(this ContactCard c, Email e)
        {
            c.emails.Remove(e);
        }
        public static void remove(this Group g, ContactCard c)
        {
            c.groups.Remove(g.name);
            g.contacts.Remove(c);
        }
        public static void remove(this AddressBook a, ContactCard c)
        {
            a.contacts.Remove(c);
        }
        public static void remove(this AddressBook a, Group g)
        {
            while(g.contacts.Count > 0)
                g.remove(g.contacts[0]);
            a.groups.Remove(g);
        }

        public static void refresh(this AddressBook a)
        {
            foreach(ContactCard c in a.contacts)
                for (int i = 0; i < c.groups.Count; i++)
                    for (int j = i + 1; j < c.groups.Count; j++)
                        if (c.groups[i].ToLower().Trim().Equals(c.groups[j].ToLower().Trim()))
                        {
                            c.groups.Remove(c.groups[j]);
                            j--;
                        }

            a.groups = new List<Group>();
            foreach (ContactCard c in a.contacts)
                foreach (string name in c.groups)
                    getGroupByName(a, name).add(c);
            
            sort(a);
        }
        public static void sort(this AddressBook a)
        {
            a.contacts.Sort();
            a.groups.Sort();
            foreach (Group g in a.groups)
                g.contacts.Sort();
        }
    }
}
